package myutil.concurrency;

import java.util.Random;

/**
 *
 *
 *
 * @author Hlib_Babii
 *
 */
public class ConcurrentCircularBufferQueue2 {

    private int[] buffer = null;

    private int head = 0;
    private int tail = 0;
    private boolean empty = true;

    public ConcurrentCircularBufferQueue2(int size) {
        buffer = new int[size];
    }

    /**
     *
     * Inserts the specified element into this queue, waiting if necessary for
     * space to become available.
     *
     * @param e the element to add
     *
     */
    public synchronized void put(int e) {
            while (head == tail && !empty) {
                System.out.println("Queue is full...");
                try {
                    this.wait();
                } catch (InterruptedException ex) {
                }
            }
            buffer[tail++] = e;
            if (tail == buffer.length) {
                tail = 0;
            }
            empty = false;
            this.notifyAll();
    }

    /**
     * Retrieves and removes the head of this queue, waiting if necessary until
     * an element becomes available.
     *
     * @return the head of the queue
     */
    public synchronized int take() {
        int toReturn;
        while (empty) {
            System.out.println("Queue is empty...");
            try {
                this.wait();
            } catch (InterruptedException ex) {
            }
        }
        toReturn = buffer[head++];
        if (head == buffer.length) {
            head = 0;
        }
        if (head == tail) {
            empty = true;
        }
        this.notifyAll();

        return toReturn;
    }

    public static class Producer implements Runnable {

        final Random random = new Random();
        ConcurrentCircularBufferQueue queue;

        Producer(ConcurrentCircularBufferQueue queue) {
            this.queue = queue;
        }
//        ArrayBlockingQueue<Integer> queue;
//
//        Producer(ArrayBlockingQueue<Integer> queue) {
//            this.queue = queue;
//        }

        @Override
        public void run() {
            for (int i = 0; i < 100000; i++) {
                queue.put(i);
                //Thread.sleep(random.nextInt(500));
            }
        }
    }

    public static class Consumer implements Runnable {

        final Random random = new Random();
        ConcurrentCircularBufferQueue queue;

        Consumer(ConcurrentCircularBufferQueue queue) {
            this.queue = queue;
        }
//        ArrayBlockingQueue<Integer> queue;
//
//        Consumer(ArrayBlockingQueue<Integer> queue) {
//            this.queue = queue;
//        }

        @Override
        public void run() {
            long start = System.currentTimeMillis();
            for (int i = 0; i < 100000; i++) {
                System.out.println(queue.take());
                //Thread.sleep(random.nextInt(500));
            }
            long end = System.currentTimeMillis();
            System.out.println(end - start);
        }
    }

    public static void main(String[] args) {
        ConcurrentCircularBufferQueue queue = new ConcurrentCircularBufferQueue(10);
        //ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(10);
        //new Thread(new Consumer(queue)).start();
        //new Thread(new Consumer(queue)).start();
        long start = System.currentTimeMillis();
        Thread consumerThread = new Thread(new Consumer(queue));
        consumerThread.start();
        new Thread(new Producer(queue)).start();
        try {
            consumerThread.join();
        } catch (InterruptedException e) {
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}
